licenseHasExpired
Email Templates​
| License Expired | Grace Period Expired |
|---|---|
![]() | ![]() |
| 30 Days Warning | 60 Days Warning |
|---|---|
![]() | ![]() |
- 🇬🇧 English
- 🇮🇹 Italian
Function Names: licenseHasExpired / testLicenseHasExpired
Author: Domenico Cerone Creation Date: 25/09/2025
Last Reviewer: Domenico Cerone
Trigger: Scheduler (onSchedule - daily at 18:00) / HTTPS (onRequest)
Purpose: Daily notification system for license management in ARShades Subscription System. This Cloud Function checks licenses daily and sends notifications to clients at key moments. It uses ZeptoMail as the primary email system and Nodemailer as fallback with specific templates for different notification types.
Detailed Functionality​
1. TRIGGER AND SCHEDULING​
- Automatic execution daily at 18:00 (Europe/Rome timezone)
- Can be executed manually via HTTP endpoint for testing
- Execution region: europe-central2
2. LICENSE RETRIEVAL AND ANALYSIS​
- Queries 'ARShadesSubscriptions' collection for all licenses
- Filters only licenses with "Active" or "Expired" status
- Verifies presence of valid expiration date
- Handles different timestamp formats for expiration date
- Calculates remaining days until expiration
3. PRELIMINARY OPTIMIZATION​
- Checks if notifications need to be sent BEFORE expensive database queries
- Verifies if license is at 60 days, 30 days from expiration, just expired, or 15 days after expiration
- Skips processing if no notification is needed
4. NOTIFICATION TYPES​
a. 60 Days Warning (SixtyDaysWarning)
- Sent when exactly 60 days remain until expiration
- Template: SIXTY_DAYS
b. 30 Days Warning (ThirtyDaysWarning)
- Sent when exactly 30 days remain until expiration
- Template: THIRTY_DAYS
c. Expiration Notification (Expired)
- Sent when license expires (endDate reached)
- Updates license status to 'Expired'
- Template: EXPIRED
d. Grace Period End Notification (GracePeriodEnd)
- Sent 15 days after expiration
- Verifies that status is 'Expired'
- Template: GRACE_PERIOD_END
5. PRELIMINARY CHECKS​
- Verifies if email sending is blocked (blockSendMail = true)
- Checks if notification of same type was already sent
- Verifies presence of client reference (clientRef)
6. CLIENT DATA AND PROFILES RETRIEVAL​
- Retrieves client document from 'Client' collection
- Gets list of catalogs associated with license (supports different storage formats)
- Extracts associated profiles (allProfileList and mainProfileList)
7. NOTIFICATION PREFERENCE CHECK​
- For each profile in allProfileList, checks if licenseHasExpired notification is enabled
- Verifies
notification_types.licenseHasExpiredfield in each profile - If notification is disabled (false): skips that profile without sending email
- If notification is enabled (true): proceeds with access verification
- Provides clear logging about notification preference status for each profile
8. EMAIL RECIPIENTS SELECTION (UPDATED LOGIC)​
- For each profile in allProfileList, verifies access to license catalogs
- IMPORTANT: Selects ONLY profiles with 'Cliente' role (excludes Admin, etc.)
- Verifies intersection between profile catalogs and license catalogs
- NEW LOGIC: If no profile has specific access BUT profiles exist in mainProfileList, email is still sent
- SKIPS ONLY if: profilesWithAccess.length === 0 AND mainProfileList.length === 0
- Logs debug details for each analyzed profile
- Handles errors for profiles not found or not accessible
9. EMAIL DATA PREPARATION​
- Formats expiration and grace period dates in Italian format
- Retrieves main profile information for personalization
- Prepares TO (profiles with access), CC (main profiles) and BCC (admin) recipients
- FIX APPLIED: Resolved licenseName/formattedLicenseName variable scoping conflict
10. EMAIL SENDING WITH ZEPTOMAIL​
- Uses appropriate template for each notification type
- Passes necessary variables to template (license name, main profile, dates)
- Handles TO, CC and BCC with full names when available
11. IMPROVED FALLBACK HANDLING​
- In case of ZeptoMail error, uses Nodemailer with specific templates for each notification type
- NEW FALLBACK TEMPLATES:
- SixtyDaysWarning: "Warning: License will expire in 60 days" (orange)
- ThirtyDaysWarning: "Urgent: License will expire in 30 days" (dark orange)
- Expired: "EXPIRED: License expired today" (red, with grace period info)
- GracePeriodEnd: "CRITICAL: Grace period ended" (dark red, service suspended)
- Professional HTML templates with appropriate colors and clean layout
- Correct dates and content for each scenario (future expiration vs already expired)
- Sent only to profiles in mainProfileList
- Logs errors from both main system and fallback
12. LICENSE DOCUMENT UPDATE​
- After successful sending, updates 'notifications' array
- Adds new object with specific notification type and sendDate
- If expiration notification, also updates status to 'Expired'
13. ADVANCED LOGGING AND MONITORING​
- Logs complete details of each process phase
- Tracks successes, errors and fallback attempts
- Produces categorized final summary (emails sent, no action, skipped, etc.)
- Provides aggregate statistics by result type
Input (Payload)​
Scheduler Function (licenseHasExpired)​
- Automatic trigger: No input required (scheduled execution)
- Schedule: Daily at 18:00 Europe/Rome timezone
HTTP Function (testLicenseHasExpired)​
- Method: GET
- Headers: None required
- Body: None required
- Parameters: None required
Output (Success)​
Scheduler Function​
{
"success": true,
"processed": true,
"results": [
{
"licenseId": "license123",
"licenseName": "ARShades Pro - Standard",
"recipients": ["user@example.com"],
"success": true
}
]
}
HTTP Function​
{
"success": true,
"message": "Controllo licenze scadute completato con successo",
"results": [
{
"licenseId": "license123",
"licenseName": "ARShades Pro - Standard",
"recipients": ["user@example.com"],
"success": true,
"fallback": false
}
]
}
Response Properties:
success(boolean): Indicates if the operation was completed successfullymessage(string): Descriptive message about the operation resultresults(array): Array of processed licenses with detailslicenseId(string): Unique identifier of the processed licenselicenseName(string): Name and type of the licenserecipients(array): List of email addresses that received the notificationfallback(boolean): Indicates if Nodemailer fallback was used
Testing​
URL (if HTTPS)​
Development: http://127.0.0.1:5001/arshadesstaging/us-central1/testLicenseHasExpired
Test with Emulator​
-
Start Firebase emulators:
firebase emulators:start --only functions -
Set Node.js version:
nvm use 20 -
Test with curl:
curl "http://127.0.0.1:5001/arshadesstaging/us-central1/testLicenseHasExpired"
Postman Testing​
- Method: GET
- URL:
http://127.0.0.1:5001/arshadesstaging/us-central1/testLicenseHasExpired - Headers: None required
- Body: None required
Deploy Command​
firebase deploy --only functions:licenseHasExpired,functions:testLicenseHasExpired
Production URL​
Scheduled Function: https://europe-central2-arshades-7e18a.cloudfunctions.net/licenseHasExpired (Reference only - not directly callable)
Test Function: https://europe-central2-arshades-7e18a.cloudfunctions.net/testLicenseHasExpired
MAIL_TEMPLATE_KEYS​
- EXPIRED:
13ef.8598f19fbcc5adb.k1.9902c130-46a8-11f0-900d-dad70ff08860.1975e5ffcc38 - SIXTY_DAYS:
13ef.8598f19fbcc5adb.k1.c7e1eee0-46d0-11f0-900d-dad70ff08860.1975f6754ce - THIRTY_DAYS:
13ef.8598f19fbcc5adb.k1.dec244d1-46d4-11f0-900d-dad70ff08860.1975f82218d - GRACE_PERIOD_END:
13ef.8598f19fbcc5adb.k1.13d51510-46d7-11f0-900d-dad70ff08860.1975f9098e1
Function Names: licenseHasExpired / testLicenseHasExpired
Autore: Domenico Cerone Data di creazione: 25/09/2025
Last Reviewer: Domenico Cerone
Trigger: Scheduler (onSchedule - giornaliero alle 18:00) / HTTPS (onRequest)
Purpose: Sistema di notifica giornaliero per la gestione delle licenze nel ARShades Subscription System. Questa Cloud Function controlla giornalmente le licenze e invia notifiche ai clienti in momenti chiave. Utilizza ZeptoMail come sistema di email principale e Nodemailer come fallback con template specifici per diversi tipi di notifica.
Funzionamento Dettagliato​
1. TRIGGER E SCHEDULING​
- Esecuzione automatica giornaliera alle 18:00 (fuso orario Europe/Rome)
- Può essere eseguita manualmente tramite endpoint HTTP per test
- Regione di esecuzione: europe-central2
2. RECUPERO E ANALISI LICENZE​
- Interroga la collezione 'ARShadesSubscriptions' per tutte le licenze
- Filtra solo licenze con status "Active" o "Expired"
- Verifica la presenza di una data di scadenza valida
- Gestisce diversi formati di timestamp per la data di scadenza
- Calcola i giorni rimanenti fino alla scadenza
3. OTTIMIZZAZIONE PRELIMINARE​
- Verifica se è necessario inviare notifiche PRIMA di fare query costose al database
- Controlla se la licenza è a 60 giorni, 30 giorni dalla scadenza, appena scaduta o a 15 giorni dopo la scadenza
- Salta l'elaborazione se non è necessaria alcuna notifica
4. TIPI DI NOTIFICA​
a. Notifica 60 giorni prima della scadenza (SixtyDaysWarning)
- Inviata quando mancano esattamente 60 giorni alla scadenza
- Template: SIXTY_DAYS
b. Notifica 30 giorni prima della scadenza (ThirtyDaysWarning)
- Inviata quando mancano esattamente 30 giorni alla scadenza
- Template: THIRTY_DAYS
c. Notifica di scadenza (Expired)
- Inviata quando la licenza scade (endDate raggiunta)
- Aggiorna lo status della licenza a 'Expired'
- Template: EXPIRED
d. Notifica fine periodo di grazia (GracePeriodEnd)
- Inviata 15 giorni dopo la scadenza
- Verifica che lo status sia 'Expired'
- Template: GRACE_PERIOD_END
5. CONTROLLI PRELIMINARI​
- Verifica se l'invio delle email è bloccato (blockSendMail = true)
- Controlla se è già stata inviata una notifica dello stesso tipo
- Verifica la presenza del riferimento al cliente (clientRef)
6. RECUPERO DATI CLIENTE E PROFILI​
- Recupera il documento cliente da 'Client'
- Ottiene la lista dei cataloghi associati alla licenza (supporta diversi formati di memorizzazione)
- Estrae i profili associati (allProfileList e mainProfileList)
7. CONTROLLO PREFERENZE NOTIFICHE​
- Per ogni profilo in allProfileList, controlla se la notifica licenseHasExpired è abilitata
- Verifica il campo
notification_types.licenseHasExpiredin ogni profilo - Se la notifica è disabilitata (false): salta quel profilo senza inviare email
- Se la notifica è abilitata (true): procede con la verifica dell'accesso
- Fornisce logging chiaro sullo stato delle preferenze di notifica per ogni profilo
8. SELEZIONE DESTINATARI EMAIL (LOGICA AGGIORNATA)​
- Per ogni profilo in allProfileList, verifica l'accesso ai cataloghi della licenza
- IMPORTANTE: Seleziona SOLO i profili con ruolo 'Cliente' (esclude Admin, ecc.)
- Verifica l'intersezione tra i cataloghi del profilo e quelli della licenza
- NUOVA LOGICA: Se nessun profilo ha accesso specifico MA esistono profili in mainProfileList, l'email viene comunque inviata
- SALTA SOLO se: profilesWithAccess.length === 0 AND mainProfileList.length === 0
- Registra dettagli di debug per ogni profilo analizzato
- Gestisce errori per profili non trovati o non accessibili
9. PREPARAZIONE DATI EMAIL​
- Formatta le date di scadenza e periodo di grazia in formato italiano
- Recupera informazioni del profilo principale per personalizzazione
- Prepara destinatari TO (profili con accesso), CC (profili principali) e BCC (admin)
- FIX APPLICATO: Risolto conflitto di scoping della variabile licenseName/formattedLicenseName
10. INVIO EMAIL CON ZEPTOMAIL​
- Utilizza il template appropriato per ogni tipo di notifica
- Passa le variabili necessarie al template (nome licenza, profilo principale, date)
- Gestisce TO, CC e BCC con nomi completi quando disponibili
11. GESTIONE FALLBACK MIGLIORATA​
- In caso di errore con ZeptoMail, utilizza Nodemailer con template specifici per ogni tipo di notifica
- NUOVI TEMPLATE FALLBACK:
- SixtyDaysWarning: "Avviso: La licenza scadrà tra 60 giorni" (arancione)
- ThirtyDaysWarning: "Urgente: La licenza scadrà tra 30 giorni" (arancione scuro)
- Expired: "SCADUTA: La licenza è scaduta oggi" (rosso, con info periodo di grazia)
- GracePeriodEnd: "CRITICO: Periodo di grazia terminato" (rosso scuro, servizio sospeso)
- Template HTML professionali con colori appropriati e layout pulito
- Date e contenuti corretti per ogni scenario (scadenza futura vs già scaduta)
- Viene inviata solo ai profili in mainProfileList
- Registra errori sia del sistema principale che del fallback
12. AGGIORNAMENTO DOCUMENTO LICENZA​
- Dopo l'invio con successo, aggiorna l'array 'notifications'
- Aggiunge un nuovo oggetto con il tipo specifico di notifica e sendDate
- Se è una notifica di scadenza, aggiorna anche lo status a 'Expired'
13. LOGGING E MONITORAGGIO AVANZATO​
- Registra dettagli completi di ogni fase del processo
- Traccia successi, errori e tentativi di fallback
- Produce un riepilogo finale categorizzato (email inviate, nessuna azione, saltate, ecc.)
- Fornisce statistiche aggregate per tipo di risultato
Input (Payload)​
Funzione Scheduler (licenseHasExpired)​
- Trigger automatico: Nessun input richiesto (esecuzione pianificata)
- Schedule: Giornaliero alle 18:00 fuso orario Europe/Rome
Funzione HTTP (testLicenseHasExpired)​
- Metodo: GET
- Headers: Nessuno richiesto
- Body: Nessuno richiesto
- Parametri: Nessuno richiesto
Output (Successo)​
Funzione Scheduler​
{
"success": true,
"processed": true,
"results": [
{
"licenseId": "license123",
"licenseName": "ARShades Pro - Standard",
"recipients": ["user@example.com"],
"success": true
}
]
}
Funzione HTTP​
{
"success": true,
"message": "Controllo licenze scadute completato con successo",
"results": [
{
"licenseId": "license123",
"licenseName": "ARShades Pro - Standard",
"recipients": ["user@example.com"],
"success": true,
"fallback": false
}
]
}
Proprietà Risposta:
success(boolean): Indica se l'operazione è stata completata con successomessage(string): Messaggio descrittivo sul risultato dell'operazioneresults(array): Array delle licenze elaborate con dettaglilicenseId(string): Identificatore unico della licenza elaboratalicenseName(string): Nome e tipo della licenzarecipients(array): Lista degli indirizzi email che hanno ricevuto la notificafallback(boolean): Indica se è stato utilizzato il fallback Nodemailer
Testing​
URL (if HTTPS)​
Development: http://127.0.0.1:5001/arshadesstaging/us-central1/testLicenseHasExpired
Test con Emulator​
-
Avvia gli emulatori Firebase:
firebase emulators:start --only functions -
Imposta la versione Node.js:
nvm use 20 -
Test con curl:
curl "http://127.0.0.1:5001/arshadesstaging/us-central1/testLicenseHasExpired"
Test Postman​
- Metodo: GET
- URL:
http://127.0.0.1:5001/arshadesstaging/us-central1/testLicenseHasExpired - Headers: Nessuno richiesto
- Body: Nessuno richiesto
Deploy Command​
firebase deploy --only functions:licenseHasExpired,functions:testLicenseHasExpired
Production URL​
Funzione Schedulata: https://europe-central2-arshades-7e18a.cloudfunctions.net/licenseHasExpired (Solo riferimento - non direttamente chiamabile)
Funzione Test: https://europe-central2-arshades-7e18a.cloudfunctions.net/testLicenseHasExpired
MAIL_TEMPLATE_KEYS​
- EXPIRED:
13ef.8598f19fbcc5adb.k1.9902c130-46a8-11f0-900d-dad70ff08860.1975e5ffcc38 - SIXTY_DAYS:
13ef.8598f19fbcc5adb.k1.c7e1eee0-46d0-11f0-900d-dad70ff08860.1975f6754ce - THIRTY_DAYS:
13ef.8598f19fbcc5adb.k1.dec244d1-46d4-11f0-900d-dad70ff08860.1975f82218d - GRACE_PERIOD_END:
13ef.8598f19fbcc5adb.k1.13d51510-46d7-11f0-900d-dad70ff08860.1975f9098e1



